package top.jle.wx.proxy.facade.impl;

import com.alibaba.fastjson2.JSONObject;
import top.jle.jpa.basic.util.EncryptUtils;
import top.jle.jpa.basic.util.ExceptionUtils;
import top.jle.wx.proxy.constant.SNSScope;
import top.jle.wx.proxy.constant.URLTemplates;
import top.jle.wx.proxy.domain.fans.FansInfo;
import top.jle.wx.proxy.exception.ExceptionCode;
import top.jle.wx.proxy.facade.BasicFacade;
import top.jle.wx.proxy.feign.BasicAPIFeign;
import top.jle.wx.proxy.feign.ResultCheckUtil;
import top.jle.wx.proxy.util.GlobalThreadLocalUtil;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

@Component("basicFacade")

public class BasicFacadeImpl implements BasicFacade {

    private static final Logger LOGGER = LoggerFactory.getLogger(BasicFacadeImpl.class);

    @Autowired
    private BasicAPIFeign basicAPIFeign;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;



    @Override
    public String getAccessToken(){
        String appId = GlobalThreadLocalUtil.getAppId();
        String secret = GlobalThreadLocalUtil.getSecret();
        if(StringUtils.isBlank(appId) || StringUtils.isBlank(secret)){
            ExceptionUtils.throwInvalidParamException();
        }
        String key = "AccessToken_"+appId;
        if(Boolean.TRUE.equals(stringRedisTemplate.hasKey(key))){
            return stringRedisTemplate.opsForValue().get(key);
        }else{
            JSONObject result = basicAPIFeign.getAccessToken(appId,secret);
            LOGGER.info(result.toJSONString());
            if(result.containsKey("errcode")){
                ExceptionUtils.throwException(ExceptionCode.GET_ACCESS_TOKEN_FAIL);
            }
            String accessToken = result.getString("access_token");
            Long expireSeconds = result.getLong("expires_in");
            stringRedisTemplate.opsForValue().set(key,accessToken,expireSeconds-100, TimeUnit.SECONDS);
            return accessToken;
        }

    }


    private String getJSAPITicket(String appId){
        String key = "JSAPITicket_"+appId;
        if(Boolean.TRUE.equals(stringRedisTemplate.hasKey(key))){
            return stringRedisTemplate.opsForValue().get(key);
        }else{
            JSONObject result = basicAPIFeign.getJSAPITicket();
            LOGGER.info(result.toJSONString());
            if(!"0".equals(result.getString("errcode"))){
                ExceptionUtils.throwException(ExceptionCode.GET_JSAPI_TICKET_FAIL);
            }
            String ticket = result.getString("ticket");
            Long expireSeconds = result.getLong("expires_in");
            stringRedisTemplate.opsForValue().set(key,ticket,expireSeconds-100, TimeUnit.SECONDS);
            return ticket;
        }
    }

    @Override
    public SNSScope.JSSDKConfig getJSSDKConf(String jsURL) {
        String appId = GlobalThreadLocalUtil.getAppId();
        SNSScope.JSSDKConfig config = new SNSScope.JSSDKConfig();
        String noncestr = "top.jle.wx";
        Long time = System.currentTimeMillis() / 1000;
        String beforeSignature = "";
        String signature;
        String jsAPITicket = getJSAPITicket(appId);
        // 注意这里参数名必须全部小写，且必须有序
        beforeSignature = "jsapi_ticket=" + jsAPITicket + "&noncestr=" + noncestr + "&timestamp="
                + time + "&url=" + URLDecoder.decode(jsURL, StandardCharsets.UTF_8);
        signature = EncryptUtils.encodeSHA(beforeSignature);
        config.setDebug(false);
        config.setAppId(appId);
        config.setTimestamp(time);
        config.setNonceStr(noncestr);
        config.setSignature(signature);
        return config;
    }

    @Override
    public String convertUrlToOauthUrl(String url, String state) {
        return URLTemplates.URL_OAUTH2_CODE.replace("APPID", GlobalThreadLocalUtil.getAppId())
                .replace("REDIRECT_URI", URLEncoder.encode(url, StandardCharsets.UTF_8))
                .replace("SCOPE", SNSScope.SNSAPI_USERINFO).replace("STATE",state);
    }

    @Override
    public String convertUrlToSNSBASEUrl(String url, String state) {
        return URLTemplates.URL_OAUTH2_CODE.replace("APPID",GlobalThreadLocalUtil.getAppId())
                .replace("REDIRECT_URI", URLEncoder.encode(url, StandardCharsets.UTF_8))
                .replace("SCOPE", SNSScope.SNSAPI_BASE).replace("STATE", state);
    }

    @Override
    public FansInfo loadSnsUserInfo(String code){
        if(StringUtils.isBlank(code)){
            ExceptionUtils.throwInvalidParamException();
        }
        JSONObject result = new JSONObject();
        String appId = GlobalThreadLocalUtil.getAppId();
        String secret = GlobalThreadLocalUtil.getSecret();

        JSONObject snsCache;
//        if(stringRedisTemplate.hasKey(key)){
//            String r = stringRedisTemplate.opsForValue().get(key);
//            snsCache = JSONObject.parseObject(r);
//
//        }else{
        snsCache = basicAPIFeign.loadSnsInfo(appId,secret,code);
        LOGGER.info("授权结果:{}",snsCache.toJSONString());
        if(!ResultCheckUtil.check(snsCache)){
            ExceptionUtils.throwException(ExceptionCode.OAUTH_CODE_INVALID);
        }
//            long expire_seconds = snsCache.getLong("expires_in");
//            stringRedisTemplate.opsForValue().set(key,snsCache.toJSONString(),expire_seconds-100,TimeUnit.SECONDS);

        String scope = snsCache.getString("scope");
        String openId = snsCache.getString("openid");
        if(SNSScope.SNSAPI_BASE.equals(scope)){
            //仅返回OPENID
            result.put("openid",snsCache.getString("openid"));
        }else{
            result = basicAPIFeign.loadUserInfoBySNS(snsCache.getString("access_token"),openId);
            LOGGER.info("获取用户信息:{}",result.toJSONString());
            if(!ResultCheckUtil.check(result)){
                ExceptionUtils.throwException(ExceptionCode.OAUTH_CODE_INVALID);
            }
        }
        return result.toJavaObject(FansInfo.class);
    }

    @Override
    public SNSScope.Code2SessionResponse code2Session(String jsCode){
        if(StringUtils.isBlank(jsCode)){
            ExceptionUtils.throwInvalidParamException();
        }
        JSONObject result = basicAPIFeign.jsCode2Session(jsCode);
        if(!ResultCheckUtil.check(result)){
            ExceptionUtils.throwException(ExceptionCode.JS_CODE_2_SESSION_FAIL);
        }
        return result.toJavaObject(SNSScope.Code2SessionResponse.class);
    }

}
